#include "spl.includes.h"
#include "splib.h"

using namespace SPLib;
using namespace SPLib::Ole;

static void CopyFormatEtc(FORMATETC *dest, FORMATETC *source)
{
    *dest = *source;

    if (source->ptd) {
        dest->ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(sizeof(DVTARGETDEVICE));
        *(dest->ptd) = *(source->ptd);
    }
}

EnumFormatEtc::EnumFormatEtc(FORMATETC *pFormatEtc, int nNumFormats)
{
    m_lRefCount = 1;
    m_nIndex = 0;
    m_nNumFormats = nNumFormats;
    m_pFormatEtc = new FORMATETC[nNumFormats];

    for (int i = 0; i < nNumFormats; ++i) {
        CopyFormatEtc(&m_pFormatEtc[i], &pFormatEtc[i]);
    }
}

EnumFormatEtc::~EnumFormatEtc()
{
    if (m_pFormatEtc) {
        for (ULONG i = 0; i < m_nNumFormats; ++i) {
            if (m_pFormatEtc[i].ptd)
                CoTaskMemFree(m_pFormatEtc[i].ptd);
        }

        delete[] m_pFormatEtc;
    }
}

ULONG EnumFormatEtc::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

ULONG EnumFormatEtc::Release()
{
    LONG count = InterlockedDecrement(&m_lRefCount);

    if (count) {
        return count;
    } else {
        delete this;
        return 0;
    }
}

HRESULT EnumFormatEtc::QueryInterface(REFIID iid, void **ppvObjectOut)
{
    if (iid == IID_IEnumFORMATETC || iid == IID_IUnknown) {
        AddRef();
        *ppvObjectOut = this;
        return S_OK;
    } else {
        *ppvObjectOut = 0;
        return E_NOINTERFACE;
    }
}

HRESULT EnumFormatEtc::Next(ULONG celt, FORMATETC *pFormatEtcOut,
    ULONG *pCeltFetchedOut)
{
    ULONG copied = 0;

    if (!celt || !pFormatEtcOut)
        return E_INVALIDARG;

    while (m_nIndex < m_nNumFormats && copied < celt)
        CopyFormatEtc(&pFormatEtcOut[copied++], &m_pFormatEtc[m_nIndex++]);

    if (pCeltFetchedOut)
        *pCeltFetchedOut = copied;

    return copied == celt ? S_OK : S_FALSE;
}

HRESULT WINAPI EnumFormatEtc::Skip(ULONG celt)
{
    m_nIndex += celt;
    return m_nIndex <= m_nNumFormats ? S_OK : S_FALSE;
}

HRESULT WINAPI EnumFormatEtc::Reset()
{
    m_nIndex = 0;
    return S_OK;
}

HRESULT WINAPI EnumFormatEtc::Clone(IEnumFORMATETC **ppEnumFormatEtcOut)
{
    *ppEnumFormatEtcOut = new EnumFormatEtc(m_pFormatEtc, m_nNumFormats);
    ((EnumFormatEtc*)*ppEnumFormatEtcOut)->m_nIndex = m_nIndex;

    return *ppEnumFormatEtcOut > 0 ? S_OK : E_OUTOFMEMORY;
}